{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# other FullControl functions\n",
    "\n",
    "these other functions support the design process in a range of ways\n",
    "\n",
    "they do not give exhaustive functionality, but highlight some useful concepts\n",
    "\n",
    "for advanced design, you will likely create new custom functions that do things like these\n",
    "\n",
    "<*this document is a jupyter notebook - if they're new to you, check out how they work:\n",
    "[link](https://www.google.com/search?q=ipynb+tutorial),\n",
    "[link](https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb),\n",
    "[link](https://colab.research.google.com/)*>\n",
    "\n",
    "*run all cells in this notebook in order (keep pressing shift+enter)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'google.colab' in str(get_ipython()):\n  !pip install git+https://github.com/FullControlXYZ/fullcontrol --quiet\nimport fullcontrol as fc"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### fc.linspace()\n",
    "\n",
    "linspace is a common function included in numpy and other libraries\n",
    "\n",
    "it creates a list of evenly spaced numbers between defined start and end numbers\n",
    "\n",
    "it's been created in FullControl to reduce the need to import other packages\n",
    "\n",
    "```\n",
    "linspace(start, end, number of points)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(fc.linspace(1.5,2.5,11))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### travel to a point with fc.travel_to()\n",
    "\n",
    "in addition to turning the extruder on and off directly with an Extruder object, the 'travel_to' function can be used for the specific case of turning extrusion off, moving to a single point, then turning extrusion on\n",
    "\n",
    "this function returns a list of three steps: [Extruder(on=False), Point, Extruder(on=True)]\n",
    "\n",
    "since it returns a list, extend() must be used instead of append() when adding the returned steps to an existing list of steps\n",
    "\n",
    "if a list of steps is supplied to the function, the first point in the list is used"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps_layer_1 = [fc.Point(x=0, y=0, z=0), fc.Point(x=5, y=20), fc.Point(x=10, y=0)]\n",
    "steps_layer_2 = [fc.Point(x=0, y=0, z=0.4), fc.Point(x=5, y=20), fc.Point(x=10, y=0)]\n",
    "steps = steps_layer_1 + fc.travel_to(steps_layer_2) + steps_layer_2\n",
    "steps.extend(fc.travel_to(fc.Point(z=5)))\n",
    "fc.transform(steps, 'plot', fc.PlotControls(color_type='print_sequence'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### define relative points with fc.relative_point()\n",
    "\n",
    "define a new point relative to a reference point. this only works if all of x y z attributes are defined for the reference point. it is possible to supply a reference list instead of a reference point, in which case the function uses the last point in the list as the reference point \n",
    "\n",
    "see the [design tips tutorial](https://colab.research.google.com/github/FullControlXYZ/fullcontrol/blob/master/tutorials/colab/design_tips_colab.ipynb) for a way to define absolute points (P) and relative points (R) extremely concisely with *steps.append(****P****(x,y,z))* and *steps.append(****R****(x,y,z))* respectively"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = [fc.Point(x=50, y=50, z=0.2), fc.Point(x=50, y=60, z=0.2)]\n",
    "steps.append(fc.relative_point(steps[-1], 10, 0, 0))\n",
    "steps.append(fc.relative_point(steps, 0, -10, 0))\n",
    "# to be extra concise, assign the the relative_point function to R\n",
    "for step in steps: print(step)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### check a design with fc.check()\n",
    "\n",
    "this function can be used to check a design. the current implementation of this function simply summarises the objects in the design and checks that you haven't accidentally added a list of objects as a single entry in the design. the example below shows how this mistake is easy to make if you used append() instead of extend() when adding a list of extra steps to an existing list of steps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = []\n",
    "steps.append(fc.Point(x=0, y=0, z=0))\n",
    "steps.append(fc.Extruder(on=False))\n",
    "steps.append(fc.Point(x=10))\n",
    "steps.append(fc.Extruder(on=False))\n",
    "steps.append(fc.Fan(speed_percent=85))\n",
    "steps.append(fc.Point(x=20))\n",
    "print('check 1:')\n",
    "fc.check(steps)\n",
    "steps.extend(fc.rectangleXY(fc.Point(x=20, y=0, z=0), x_size=10, y_size=10)) # fc.rectangleXY() returns a list of five points\n",
    "print('\\ncheck 2 (extended steps with a list):')\n",
    "fc.check(steps)\n",
    "steps.append(fc.rectangleXY(fc.Point(x=20, y=0, z=0), x_size=10, y_size=10))\n",
    "print('\\ncheck 3: (appended steps with a list)')\n",
    "fc.check(steps)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### flatten a ***design*** that contains lists of objects with fc.flatten()\n",
    "\n",
    "FullControl ***designs*** must be 1D arrays of FullControl objects. however, it might be conceptually useful to create a ***design*** as a list of actions, where each action may be formed of several steps. if so, you can use fc.flatten to turn a collection of lists into a 1D list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = []\n",
    "steps.append(fc.Point(x=0, y=0, z=0))\n",
    "steps.append([fc.Point(x=10, y=10), fc.ManualGcode(text=\"G4 P2000 ; pause for 2 seconds\")])\n",
    "steps.append([fc.Point(x=20, y=20), fc.ManualGcode(text=\"G4 P2000 ; pause for 2 seconds\")])\n",
    "steps.append([fc.Point(x=30, y=30), fc.ManualGcode(text=\"G4 P2000 ; pause for 2 seconds\")])\n",
    "print('original steps:')\n",
    "for step in steps:\n",
    "    print(repr(step))\n",
    "\n",
    "print(\"\\noriginal steps 'check':\")\n",
    "fc.check(steps)\n",
    "\n",
    "flat_steps = fc.flatten(steps)\n",
    "print('\\nflat steps:')\n",
    "for step in flat_steps:\n",
    "    print(repr(step))\n",
    "print(\"\\nflat steps 'check':\")\n",
    "fc.check(flat_steps)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### find the first or last point in a ***design*** with fc.first_point() and fc.last_point() \n",
    "\n",
    "these functions find the first or last point objects in the design\n",
    "\n",
    "it can be set to find the first/last point object of any kind or the first/last one with all of x y z defined"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = [fc.Fan(speed_percent=75), fc.Point(x=1), fc.Point(y=3), fc.Point(x=1, y=1, z=1), fc.Point(x=1, y=1, z=2), fc.Point(x=2), fc.Fan(speed_percent=100)]\n",
    "print(\"first step in the design: \" + str(type(steps[0]).__name__))\n",
    "print(\"first point in the design (not fully defined): \" + str(fc.first_point(steps, fully_defined=False)))\n",
    "print(\"first point in the design (fully defined): \" + str(fc.first_point(steps, fully_defined=True)))\n",
    "print(\"last point in the design (not fully defined): \" + str(fc.last_point(steps, fully_defined=False)))\n",
    "print(\"last point in the design (fully defined): \" + str(fc.last_point(steps, fully_defined=True)))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### extract points from a ***design*** with fc.point_only()\n",
    "\n",
    "this function removes all objects from the ***design*** except Point objects\n",
    "\n",
    "it's useful for creating plots or analyzing the geometry, etc.\n",
    "\n",
    "it can return fully defined points (all of x y z defined - carried over from previous points if not set by the user) or it can return the points exactly as they were created"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = [fc.Point(x=1, y=1, z=0), fc.Fan(speed_percent=75), fc.Point(y=3)]\n",
    "print(\"steps: \" + str(steps))\n",
    "print(\"points in steps (no tracking): \" + str(fc.points_only(steps, track_xyz=False)))\n",
    "print(\"points in steps (tracking): \" + str(fc.points_only(steps, track_xyz=True)))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### export and import a ***design*** as a .json file\n",
    "\n",
    "the export_design() exports a ***design*** to a .json file. \n",
    "\n",
    "the import_design() function loads the design back into python. it must be passed the FullControl module handle (fc in this notebook) so it can convert the .json file into the correct type of FullControl objects\n",
    "\n",
    "importing a ***design*** is useful if it is computationally demanding to generate the python list of FullControl objects. by exporting a design, you can import it and resume work without needing to repeat the computations/calculations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = [fc.Point(x=0, y=0, z=0), fc.Point(x=10), fc.Extruder(on=False), fc.Fan(speed_percent=75), fc.Point(x=20)]\n",
    "fc.export_design(steps, 'my_design')\n",
    "steps_imported = fc.import_design(fc, 'my_design')\n",
    "print(fc.transform(steps_imported, 'gcode'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### bounding box\n",
    "\n",
    "find the bounding box of a design, including data for min, mid, max and range for x/y/z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = [fc.Point(x=0,y=0,z=0), fc.Point(x=10,y=10,z=10)]\n",
    "bounding_box = fc.BoundingBox()\n",
    "bounding_box.calc_bounds(steps)\n",
    "print(bounding_box)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  },
  "vscode": {
   "interpreter": {
    "hash": "2b13a99eb0d91dd901c683fa32c6210ac0c6779bab056ce7c570b3b366dfe237"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
